package tcp

import (
	"net"
	"os"
	"time"

	"github.com/kataras/golog"
)

// Client 结构体
type Client struct {
	Name              string
	ServerAddress     string
	Conn              *net.TCPConn
	logger            *golog.Logger
	shuttingDown      chan bool
	dialRetryDuration int
	readRetryTimes    int
	readRetryDuration int
	conFn             func(*net.TCPConn, *golog.Logger)
	disConFn          func(string, bool, *golog.Logger)
	pkgFn             func(*Package, *net.TCPConn, *golog.Logger)
}

// NewClient 创建一个 TCPClient 实例
func NewClient(name string, serverAddr string) *Client {
	cln := &Client{
		Name:          name,
		ServerAddress: serverAddr,
	}

	return cln
}

// SetLogger 设置日志
func (tc *Client) SetLogger(logger *golog.Logger) {
	tc.logger = logger
}

// Logger 返回日志记录器
func (tc *Client) Logger() *golog.Logger {
	if tc.logger == nil {
		tc.logger = golog.New()
		tc.logger.SetOutput(os.Stdout)
	}

	return tc.logger
}

// SetReadRetryParams 设置连接读取的重试参数
func (tc *Client) SetReadRetryParams(times, duration int) {
	tc.readRetryTimes = times
	if tc.readRetryTimes < 0 {
		tc.readRetryTimes = 10
	}
	tc.readRetryDuration = duration
	if tc.readRetryDuration < 0 {
		tc.readRetryDuration = 10
	}
}

// SetDialRetryParams 设置连接拨号的重试参数
func (tc *Client) SetDialRetryParams(duration int) {
	tc.dialRetryDuration = duration
	if tc.dialRetryDuration < 0 {
		tc.dialRetryDuration = 10
	}
}

// SetOnConnectedFn 设置当有新连接接入时的自定义处理
func (tc *Client) SetOnConnectedFn(fn func(*net.TCPConn, *golog.Logger)) {
	tc.conFn = fn
}

// SetOnDisconnectFn 设置当有连接中断时的处理，第二个参数为 true 时，为服务端主动关闭连接
func (tc *Client) SetOnDisconnectFn(fn func(string, bool, *golog.Logger)) {
	tc.disConFn = fn
}

// SetOnPkgFn 设置当解析到数据包进行处理的自定义处理
func (tc *Client) SetOnPkgFn(fn func(*Package, *net.TCPConn, *golog.Logger)) {
	tc.pkgFn = fn
}

// Start 启动 TCPClient
func (tc *Client) Start() error {
	// 自定义关闭命令
	go OnManualShutdown(tc.shuttingDown, tc.Shutdown)

	go func() {
		tc.dial()
	}()
	<-tc.shuttingDown
	return nil
}

// Shutdown 发送关闭消息
func (tc *Client) Shutdown() {
	if tc.shuttingDown == nil {
		return
	}
	if len(tc.shuttingDown) >= 1 {
		return
	}
	tc.shuttingDown <- true
}

func (tc *Client) dial() {
	// 尝试连接
	var serverAddr *net.TCPAddr
	var err error

	// 开始尝试连接，永远重试
	for len(tc.shuttingDown) < 1 {
		// 解析服务地址
		serverAddr, err = net.ResolveTCPAddr("tcp4", tc.ServerAddress)
		if err != nil {
			tc.Logger().Errorf("can resolve tcp server address %s", tc.ServerAddress)
			continue
		}

		// 尝试连接
		tc.Conn, err = net.DialTCP("tcp", nil, serverAddr)
		if err != nil {
			tc.Logger().Errorf("can not connet to tpc server %s %s", tc.ServerAddress, err)
			time.Sleep(time.Second * time.Duration(tc.dialRetryDuration))
			continue
		} else {
			tc.Logger().Infof("tcp server %s connected", tc.ServerAddress)
			if tc.conFn != nil {
				tc.conFn(tc.Conn, tc.Logger())
			} else {
				tc.Logger().Warn("connection on connected handler is not specified")
			}
			break
		}
	}

	if err == nil {
		tc.handleConn()
	}
}

func (tc *Client) pkgFnWrapper(pkg *Package, con *net.TCPConn, server *Server, logger *golog.Logger) {
	if tc.pkgFn == nil {
		logger.Warn("package handler is not specified")
	} else {
		tc.pkgFn(pkg, con, logger)
	}
}

func (tc *Client) disConFnWrapper(addr string, force bool, server *Server, logger *golog.Logger) {
	if tc.disConFn == nil {
		logger.Warn("disconnected handler is not specified")
	} else {
		tc.disConFn(addr, force, logger)
	}
}

func (tc *Client) handleConn() {
	// 处理接收
	err := HandleConnWithSig(
		tc.Conn,
		nil,
		tc.pkgFnWrapper,
		tc.disConFnWrapper,
		tc.Logger(),
		tc.readRetryTimes,
		tc.readRetryDuration,
		PackageFirstSigByte,
		PackageSecondSigByte,
	)

	if err != nil {
		tc.Logger().Warnf("proceed package failed: %s", err)
		tc.dial()
	}
}
